﻿using System.Collections.Generic;
using System.Linq;
using System.Windows;
using TextEditor.TextBoxInner.DrawingElements;
using TextEditor.TextBoxInner.Utils;

namespace TextEditor.TextBoxInner.Layers
{
	internal class TextLayer : LayerBase<TextLine>
	{
		private static readonly TextLineCreator _textLineCreator = new TextLineCreator();
		
		private readonly List<TextLine> _tempInvisibleList = new List<TextLine>(50);
		private readonly List<TextLine> _tempCandidateseList = new List<TextLine>(50);

		public TextLayer() : base(_textLineCreator)
		{ }

		public void SetLines(RenderedInfo[] lines)
		{
#if DEBUG
			//System.Diagnostics.Debug.Assert(lines.Length < Children.Count);
#endif

			try
			{
				if (lines.Length == 0)
				{
					for (int index = 0; index < Children.Count; index++)
						Children[index].SetContent(null);

					return;
				}

				_tempInvisibleList.Clear();
				_tempCandidateseList.Clear();

				int startIndex = lines[0].LineIndex;
				int endIndex = lines[lines.Length - 1].LineIndex;

				int startCandidatesIndex = -1;
//				int endCandidatesIndex = -1;

				for (int index = 0; index < Children.Count; index++)
				{
					TextLine line = Children[index];

					if (!line.IsVisible)
					{
						_tempInvisibleList.Add(line);
						continue;
					}

					if (line.LineIndex < startIndex)
					{
						_tempInvisibleList.Add(line);
						continue;
					}

					if (line.LineIndex > endIndex)
					{
						_tempInvisibleList.Add(line);
						continue;
					}

					_tempCandidateseList.Add(line);

//					endCandidatesIndex = line.LineIndex;

					if (startCandidatesIndex < 0)
						startCandidatesIndex = line.LineIndex;
				}

				for (int index = 0; index < lines.Length; index++)
				{
					RenderedInfo item = lines[index];
					int itemIndex = item.LineIndex;

					if (itemIndex == startCandidatesIndex)
					{
						for (int marker = 0; marker < _tempCandidateseList.Count; marker++)
							Children[index + marker] = _tempCandidateseList[marker];

						index += _tempCandidateseList.Count - 1;
					}
					else
					{
						int tempElementIndex = _tempInvisibleList.Count - 1;
						TextLine temp = _tempInvisibleList[tempElementIndex];
						_tempInvisibleList.RemoveAt(tempElementIndex);

						Children[index] = temp;
						temp.SetContent(item);
					}
				}

				for (int index = lines.Length; index < Children.Count; index++)
				{
					int tempElementIndex = _tempInvisibleList.Count - 1;
					TextLine temp = _tempInvisibleList[tempElementIndex];
					_tempInvisibleList.RemoveAt(tempElementIndex);

					Children[index] = temp;
					temp.SetContent(null);
				}

				for (int index = 0; index < Children.Count; index++)
					Children[index].SetTop(index * FontHelper.FontHeight);
			}
			finally
			{
				InvalidateArrange();
			}
		}

		internal CharPosition GetChartPosition(Point location)
		{
			for (int index = 0; index < Children.Count; index++)
			{
				TextLine text = Children[index];

				if (text == null || text.GetTop() > location.Y || text.GetTop() + FontHelper.FontHeight < location.Y)
					continue;

				if (string.IsNullOrEmpty(text.TextAsString.Trim()))
					return new CharPosition(text.TextAsString.Length, text.LineIndex);

				double x = GetLeft() - 2;
				int chCount = 0;
				for (int col = 0; col < text.TextAsString.Length; col++)
				{
					char ch = text.TextAsString[col];

					if (ch == '\t')
					{
						chCount += 4 - chCount % 4;
					}
					else
					{
						chCount++;
					}

					double newX = GetLeft() + chCount * FontHelper.FontWidth - 2;

					if (location.X <= (x + newX) / 2d)
						return new CharPosition(col, text.LineIndex);
					if ((x + newX) / 2d < location.X && location.X <= newX)
						return new CharPosition(col + 1, text.LineIndex);

					x = newX;
				}

				return new CharPosition(text.TextAsString.Length, text.LineIndex);
			}

			if (Children.Count > 0)
				return new CharPosition(Children.Last().TextAsString.Length, Children.Last().LineIndex);

			return null;
		}

		private class TextLineCreator : DrawingElementFactory<TextLine>
		{
			internal override TextLine Create()
			{
				return new TextLine();
			}
		}
	}
}
